import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.*;

public class FileConnModule {

}

//文件接收消息队列
//功能: 单例,持续存在,全局可用,rcv_file_queue要被多个线程读写
class ReciveFileQueue {
    static BlockingQueue<byte[]> rcv_file_queue;
    private static final ReciveFileQueue INSTANCE = new ReciveFileQueue();

    ReciveFileQueue() {
        System.out.println("ReciveFileQueue单例已创建");
        rcv_file_queue = new LinkedBlockingQueue<>();//无参构造就认为queue的长度是int的最大值
    }

    public static ReciveFileQueue getInstance() {
        return INSTANCE;
    }

    public static BlockingQueue getQueue() {
        return rcv_file_queue;
    }
}

//文件接收线程
//功能: 文件收发连接建立后,就持续执行
class ReciveFileThread implements Runnable {
    //9:49
    Socket file_conn = null;
    BufferedInputStream bis = null;

    ReciveFileThread(Socket file_conn_) {
        file_conn = file_conn_;
        try {
            bis = new BufferedInputStream(file_conn.getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        byte[] ba = null;
        while (true) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ba = NetChunkUtil.recive(bis);
            if (ba != null) {
                //向"文件接收队列"里面写入此块 //10:01
                BlockingQueue<byte[]> rcv_file_queue = ReciveFileQueue.getQueue();
                rcv_file_queue.add(ba);

                System.out.println("rcv_file_queue文件接收队列大小: " + rcv_file_queue.size());
            }else {
                //ba返回值为null,证明socket断开
                System.out.println(Thread.currentThread().getName() + "文件接收线程停止");
                break;
            }
        }
    }
}

//文件发送队列
//功能: 这个不是单例, 而是每个客户端对应一个文件发送队列
//关系: 一个发送队列, 会被3个指向这个客户端的发送线程持有
class SendFileQueue {
    BlockingQueue<byte[]> send_file_queue;
}

//文件发送队列管理Map
//功能: 是单例,管理所有客户端的文件发送队列
//参数: String客户端的ip地址, BlockingQueue<byte[]>客户端的文件发送队列
class SendFileQueueMap {
    static Map<String, BlockingQueue<byte[]>> send_file_queue_map;
    private static final SendFileQueueMap INSTANCE = new SendFileQueueMap();

    SendFileQueueMap() {
        System.out.println("SendFileQueueMap单例已创建");
        send_file_queue_map = new ConcurrentHashMap<>();//无参构造就认为queue的长度是int的最大值
    }

    public static SendFileQueueMap getInstance() {
        return INSTANCE;
    }

    public static Map<String, BlockingQueue<byte[]>> getMap() {
        return send_file_queue_map;
    }
}

//文件发送Runnable,提交给线程池执行
//此Runnable执行一次仅发送1个文件片,发完就执行完
class SendFileThread2 implements Runnable {
    String ip;//要发送给的ip
    FileHoldingListSelf FHLS;
    int serial_num;//待发送文件片的序号

    @Override
    public void run() {
        //获取"文件发送信息管理map"
        Map<String, SendFileConnInfoMap.SFInfo> send_file_conn_info_map = SendFileConnInfoMap.getMap();
        //通过ip找到要发送给的BufferedOutputStream
        SendFileConnInfoMap.SFInfo SFInfo = send_file_conn_info_map.get(ip);
        //从FileHoldingListSelf对象中,直接读取到文件片的存储绝对地址
        String file_path = FHLS.getFile_chunk_list()[serial_num].getAddr();
        //从磁盘读入文件片
        try {
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file_path));
            int total_length = bis.available();
            System.out.println("发送文件片长度为: " + total_length);
            byte[] s_ba = new byte[total_length];
            bis.read(s_ba);

            byte[] ba = MessageEncapsulationUtils.MessageToChunk(s_ba);//把文件片加上"大小"首部

            synchronized (SFInfo.bos_arr[0]){
                SFInfo.bos_arr[0].write(ba);
                SFInfo.bos_arr[0].flush();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

//文件发送线程Runnable
//功能: 作为"文件发送队列"的消费者,持续从"文件发送队列"里拿出byte[]数组然后发送
class SendFileThread implements Runnable {
    //10:25
    Socket file_conn = null;
    BufferedOutputStream bos = null;

    SendFileThread(Socket s, BufferedOutputStream b) {
        file_conn = s;
        bos = b;
    }

    @Override
    public void run() {
        while (true) {
            try {
                //纪律性睡觉
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Map<String, BlockingQueue<byte[]>> send_file_queue_map = SendFileQueueMap.getMap();
            String myKey = file_conn.getInetAddress().toString();

            //检测"发送队列管理Map"中是否还存在 "我需要的"发送队列
            if (send_file_queue_map.containsKey(myKey)) {
                //如果存在, 就从"发送队列管理Map"中拿到发送队列
                BlockingQueue<byte[]> send_file_queue = send_file_queue_map.get(myKey);
                byte[] ba = send_file_queue.poll();
                if (ba != null) {
                    try {
                        bos.write(ba);
                        bos.flush();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            } else {
                //如果不存在 "我需要的"发送队列,说明 "我" 该停止执行了
                break;//跳出while(true)循环,即结束
            }
        }
    }
}

//管理所有"文件发送连接"的map(单例)
//参数: key是客户端的ip地址, val是 3个file_conn对应的 3个BufferedOutputStream
//注意: 客户端的file_conn和ctrl_conn实际上获取到的getInetAddress()是一样的
//一个客户端对应3个文件发送连接, 所以一个客户端ip地址也就对应3个文件发送连接
class SendFileConnInfoMap {
    static class SFInfo {
        Socket[] file_conn_arr = new Socket[3];
        BufferedOutputStream[] bos_arr = new BufferedOutputStream[3];

        SFInfo(Socket[] s_arr, BufferedOutputStream[] b_arr) {
            //file_conn.getInetAddress()
            file_conn_arr[0] = s_arr[0];
            file_conn_arr[1] = s_arr[1];
            file_conn_arr[2] = s_arr[2];

            bos_arr[0] = b_arr[0];
            bos_arr[1] = b_arr[1];
            bos_arr[2] = b_arr[2];
        }
    }

    private static final SendFileConnInfoMap INSTANCE = new SendFileConnInfoMap();
    static Map<String, SFInfo> send_file_conn_info_map;

    SendFileConnInfoMap() {
        System.out.println("SendFileConnInfoMap单例已创建");
        send_file_conn_info_map = new ConcurrentHashMap<>();
    }

    static SendFileConnInfoMap getInstance() {
        return INSTANCE;
    }

    static Map<String, SFInfo> getMap() {
        return send_file_conn_info_map;
    }
}

//文件发送线程池
//功能: 执行所有文件发送线程
//注: 还未编写完毕
class SendFilePool{
    static ExecutorService send_file_pool;
    private static final SendFilePool INSTANCE = new SendFilePool();
    SendFilePool(){
        System.out.println("SendFilePool文件发送线程池已经创建");
        send_file_pool = Executors.newFixedThreadPool(12);//3的倍数,一次最多给4个客户端发送
    }
    static SendFilePool getInstance(){
        return INSTANCE;
    }
    static ExecutorService getPool(){
        return send_file_pool;
    }
}